#include   <sys/types.h>
#include   <stdarg.h>
#include   <stdio.h>
#include   <stdlib.h>
#include   <errno.h>
#include   <limits.h>
#include   <time.h>
#include   <sys/timeb.h>

#include  "SocketUtil.h"
#include "Types.h"
#include	<iostream>
//#include  "Logging.h"

typedef struct sockaddr SA;

#ifdef WIN32
//以下函数将错误码与字符串描述对应
#define E(code, s) { code, (s " [" #code " ]") }
__declspec(thread) static struct { int code; const char *msg; } windows_socket_errors[] = {
  E(WSAEINTR, "Interrupted function call"),
  E(WSAEACCES, "Permission denied"),
  E(WSAEFAULT, "Bad address"),
  E(WSAEINVAL, "Invalid argument"),
  E(WSAEMFILE, "Too many open files"),
  E(WSAEWOULDBLOCK,  "Resource temporarily unavailable"),
  E(WSAEINPROGRESS, "Operation now in progress"),
  E(WSAEALREADY, "Operation already in progress"),
  E(WSAENOTSOCK, "Socket operation on nonsocket"),
  E(WSAEDESTADDRREQ, "Destination address required"),
  E(WSAEMSGSIZE, "Message too long"),
  E(WSAEPROTOTYPE, "Protocol wrong for socket"),
  E(WSAENOPROTOOPT, "Bad protocol option"),
  E(WSAEPROTONOSUPPORT, "Protocol not supported"),
  E(WSAESOCKTNOSUPPORT, "Socket type not supported"),
  /* What's the difference between NOTSUPP and NOSUPPORT? :) */
  E(WSAEOPNOTSUPP, "Operation not supported"),
  E(WSAEPFNOSUPPORT,  "Protocol family not supported"),
  E(WSAEAFNOSUPPORT, "Address family not supported by protocol family"),
  E(WSAEADDRINUSE, "Address already in use"),
  E(WSAEADDRNOTAVAIL, "Cannot assign requested address"),
  E(WSAENETDOWN, "Network is down"),
  E(WSAENETUNREACH, "Network is unreachable"),
  E(WSAENETRESET, "Network dropped connection on reset"),
  E(WSAECONNABORTED, "Software caused connection abort"),
  E(WSAECONNRESET, "Connection reset by peer"),
  E(WSAENOBUFS, "No buffer space available"),
  E(WSAEISCONN, "Socket is already connected"),
  E(WSAENOTCONN, "Socket is not connected"),
  E(WSAESHUTDOWN, "Cannot send after socket shutdown"),
  E(WSAETIMEDOUT, "Connection timed out"),
  E(WSAECONNREFUSED, "Connection refused"),
  E(WSAEHOSTDOWN, "Host is down"),
  E(WSAEHOSTUNREACH, "No route to host"),
  E(WSAEPROCLIM, "Too many processes"),

  /* Yes, some of these start with WSA, not WSAE. No, I don't know why. */
  E(WSASYSNOTREADY, "Network subsystem is unavailable"),
  E(WSAVERNOTSUPPORTED, "Winsock.dll out of range"),
  E(WSANOTINITIALISED, "Successful WSAStartup not yet performed"),
  E(WSAEDISCON, "Graceful shutdown now in progress"),
#ifdef WSATYPE_NOT_FOUND
  E(WSATYPE_NOT_FOUND, "Class type not found"),
#endif
  E(WSAHOST_NOT_FOUND, "Host not found"),
  E(WSATRY_AGAIN, "Nonauthoritative host not found"),
  E(WSANO_RECOVERY, "This is a nonrecoverable error"),
  E(WSANO_DATA, "Valid name, no data record of requested type)"),

  { -1, NULL },
};
#undef E
_declspec(thread) char errstr[512] = {'\0'};
/** Equivalent to strerror, but for windows socket errors. */
const char *str_socket_error(int errcode)
{
  int i;
  
  for (i=0; windows_socket_errors[i].code >= 0; ++i) {
    if (errcode == windows_socket_errors[i].code)
      return windows_socket_errors[i].msg;
  }
  strerror_s(errstr, sizeof(errstr), errcode);
  return errstr;
}

#else

static __thread char t_errstrbuf[512];
const char *str_socket_error(int errcode)
{
    return strerror_r(errcode, t_errstrbuf, sizeof(t_errstrbuf));
}

#endif

const SA* sockaddr_cast(const struct sockaddr_in *addr)
{
    return static_cast<const SA*>(implicit_cast<const void*>(addr));
}

SA* sockaddr_cast(struct sockaddr_in *addr)
{
    return static_cast<SA*>(implicit_cast<void*>(addr));
}

int get_socket_error(socket_t sock)
{
    int optval; 
    socklen_t len = sizeof(optval);
    int errcode = socket_error();

    //if (ERR(WOULDBLOCK) == errcode && sock >= 0) {
    if (::getsockopt(sock, SOL_SOCKET, SO_ERROR, (SOCKOPT_T*)&optval, &len)<0) 
        return errcode;
    if (optval)
        return optval;
    //}
    return errcode;
}


int set_socket_nonblock(socket_t sock)
{
#ifdef WIN32
    u_long nonblock = 1;
    if (ioctlsocket(sock, FIONBIO, &nonblock)) {
        return -1; 
    }
#else
    int flags = ::fcntl(sock, F_GETFL, 0);
    if (flags < 0) return -1; 
    if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
        return -1; 
    }

    flags = ::fcntl(sock, F_GETFD, 0);
    flags |= FD_CLOEXEC;
    ::fcntl(sock, F_SETFD, flags);
#endif
    return 0;
}

//allow binding the same address after we close the socket
int set_socket_reuseable(socket_t sock)
{
    int on = 1;
    int res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, (socklen_t)sizeof(on));

    return res;
}


#ifdef WIN32
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
    struct _timeb tb;

    if (tv == NULL)     return -1;
    _ftime_s(&tb);
    tv->tv_sec = (long)tb.time;
    tv->tv_usec = ((int)tb.millitm) * 1000;
    return 0;
}
#endif


int get_addrinfo(const char *name, struct sockaddr_in *addrptr)
{
	struct hostent *ht;
	ht = ::gethostbyname(name);
	if (ht == NULL) {
		//LOG_DEBUG << "get socketaddr of " << name << "failed\n";
		return -1;
	}

	if (ht->h_addr != NULL)
	{
		//memcpy(addrptr, ht->h_addr, ht->h_length);
		addrptr->sin_addr.s_addr = *(u_long*)ht->h_addr;
		std::cout << "the address of "  << name << "is " 
			     << inet_ntoa(addrptr->sin_addr) << std::endl;
		return 0;
	}
	else	
		return -1;	
}

static int util_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap);
int util_snprintf(char *buf, size_t buflen, const char *format, ...)
{
	int r;
	va_list ap;
	va_start(ap, format);
	r = util_vsnprintf(buf, buflen, format, ap);
	va_end(ap);
	return r;
}

static int util_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap)
{
	int r;
	if (!buflen)
		return 0;
#ifdef _MSC_VER
	r = _vsnprintf_s(buf, buflen, buflen-1, format, ap);
	if (r < 0)
		r = _vscprintf(format, ap);
#elif defined(sgi)
	/* Make sure we always use the correct vsnprintf on IRIX */
	extern int      _xpg5_vsnprintf(char * __restrict,
		__SGI_LIBC_NAMESPACE_QUALIFIER size_t,
		const char * __restrict, /* va_list */ char *);

	r = _xpg5_vsnprintf(buf, buflen, format, ap);
#else
	r = vsnprintf(buf, buflen, format, ap);
#endif
	buf[buflen-1] = '\0';
	return r;
}

socket_t Sockets::createNonblockSocket()
{
    socket_t sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd == INVALID_SOCKET) {
        std::cerr << "create a socket failed";
    }

    set_socket_nonblock(sockfd);

    return sockfd;
}

int Sockets::connect(socket_t sockfd, const struct sockaddr_in &addr)
{
    int ret = ::connect(sockfd, sockaddr_cast(&addr), sizeof(addr));
    if (ret < 0) {
        int errcode = get_socket_error(sockfd);
        if (ERR_CONNECT_RETRIABLE(errcode))
            return CONN_RETRIABLE;
        if (ERR_CONNECT_REFUSED(errcode))
            return CONN_REFUSED;
        std::cerr << "connect error";
        return -1;
    }
    return CONN_OK;
}

int Sockets::accept(socket_t sockfd, int *confd_ptr, struct sockaddr_in *in_addr)
{
    socklen_t len = sizeof(*in_addr);
    int connfd = ::accept(sockfd, sockaddr_cast(in_addr), &len);
    if (connfd < 0) {
        int errcode = get_socket_error(sockfd);
        if (ERR_ACCEPT_RETRIABLE(errcode))
            return ACCEPT_RETRIABLE;
        std::cerr << "unexpected error of accept";
        return -1;
    }
    set_socket_nonblock(connfd);
    if (confd_ptr)  *confd_ptr = connfd;
    return ACCEPT_OK;
}

void Sockets::listen(int sockfd)
{
    int ret = ::listen(sockfd, SOMAXCONN);
    if (ret < 0) {
        std::cerr << "listen failed";
    }
}
    
void Sockets::bind(socket_t sock, const struct sockaddr_in &addr)
{
    int ret = ::bind(sock, sockaddr_cast(&addr), sizeof(addr));
    if (ret < 0)
    {
        std::cerr << " bind failed\n";
    }
}

struct sockaddr_in Sockets::getLocalAddr(socket_t sockfd)
{
    struct sockaddr_in localaddr;
    socklen_t addrlen;
    memset(&localaddr, 0x00, sizeof(localaddr));
    if (::getsockname(sockfd, sockaddr_cast(&localaddr), &addrlen))
        std::cerr << "get localaddr failed\n";

    return localaddr;
}

struct sockaddr_in Sockets::getPeerAddr(socket_t sockfd)
{
    struct sockaddr_in peeraddr;
    socklen_t addrlen;
    memset(&peeraddr, 0x00, sizeof(peeraddr));
    if (::getpeername(sockfd, sockaddr_cast(&peeraddr), &addrlen))
        std::cerr << "Socket::getPeerAddr";

    return peeraddr;
}

void Sockets::shutdownWrite(socket_t sockfd)
{
    if (::shutdown(sockfd, SHUT_WR) < 0)
    {
        std::cerr << "sockets shutwrite";
    }
}


#define	MAX_READSIZE 4096 /*  */
int Sockets::get_readable_nbytes(socket_t sockfd)
{
#if defined(FIONREAD) && defined(WIN32)
    unsigned long lng = MAX_READSIZE;
    if (ioctlsocket(sockfd, FIONREAD, &lng) < 0)
        return -1;
    return (int)lng;
#elif defined(FIONREAD)
    int n = MAX_READSIZE;
    if (ioctl(sockfd, FIONREAD, &n) < 0)
        return -1;
    return n;
#else
    return MAX_READSIZE;
#endif
}
    
bool Sockets::isSelfConnect(socket_t sockfd)
{
    struct sockaddr_in localaddr = getLocalAddr(sockfd);
    struct sockaddr_in peeraddr = getPeerAddr(sockfd);
    
    return localaddr.sin_port == peeraddr.sin_port
           && localaddr.sin_addr.s_addr == peeraddr.sin_addr.s_addr;

}
